package com.bestv.search.spider.douban.sync;

import com.bestv.search.spider.douban.model.DouBanVideoBaseInfo;
import com.bestv.search.spider.douban.model.DouBanVideoBaseInfoJSON;
import com.bestv.search.spider.douban.model.Subject;
import com.bestv.search.spider.douban.service.DouBanVideoBaseInfoManager;
import com.bestv.search.spider.douban.util.DouBanAppKeyPool;
import com.bestv.search.spider.douban.util.DouBanUtil;
import com.bestv.search.spider.util.PropertyStrategyWrapper;
import com.bestv.search.spider.util.SpiderConst;
import com.bestv.search.spider.util.SpiderUtil;
import com.bestv.search.spider.util.StringUtil;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import net.sf.json.util.PropertySetStrategy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class DouBanVideoBaseInfoSyncer extends QuartzJobBean {
    private static boolean isRunning = false;
    private final Log log = LogFactory.getLog(DouBanVideoBaseInfoSyncer.class);
    private DouBanVideoBaseInfoManager douBanVideoBaseInfoManager;
    private String infoFolder;

    @Override
    protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
        if (!isRunning) {
            isRunning = true;

            Calendar c = Calendar.getInstance();
            String fileFolderPath = getFileFolderByDateTime(c.getTime());

            // remove over 7 days' files.
            String parentFolder = fileFolderPath + File.separator + SpiderConst.POINT + SpiderConst.POINT
                    + File.separator;
            String[] overdueFolders = SpiderUtil.getOver7DaysFolders(parentFolder);
            if (overdueFolders != null) {
                for (String path : overdueFolders) {
                    SpiderUtil.deleteDirectory(parentFolder + path);
                }
            }

            String isRunningFile = fileFolderPath + File.separator + SpiderConst.ISRUNNING_FLAG_FILE;
            SpiderUtil.createFile(isRunningFile);

            log.info("Start sync all video base info from douban");
            try {
                syncAllVideoBaseInfoFromDouBan();

                // Writes videos' base info to file as JSON format
                String file = fileFolderPath + File.separator + SpiderConst.DOUBAN_ALL_VIDEOS_FILE;
                List<DouBanVideoBaseInfo> list = douBanVideoBaseInfoManager.getValidInfos();
                for (DouBanVideoBaseInfo info : list) {
                    String json = convertToJson(info);
                    SpiderUtil.addTextToFile(json, file);
                }
                SpiderUtil.deleteFile(isRunningFile);
            } finally {
                isRunning = false;
            }
            log.info("End sync all video base info from douban");
        } else {
            log.info("Last video sync process is still running");
        }
    }

    public void syncAllVideoBaseInfoFromDouBan() {
        final int maxRetryTimes = 10;
        List<DouBanVideoBaseInfo> updateList = new ArrayList<DouBanVideoBaseInfo>();
        List<DouBanVideoBaseInfo> infoList = douBanVideoBaseInfoManager.getUnSyncedInfo();
        for (int i = 0; i < infoList.size(); ) {
            DouBanVideoBaseInfo info = infoList.get(i);
            String name = StringUtil.convertToURLEncoder(info.getName(), SpiderConst.UTF_8);
            StringBuffer url = StringUtil.buildBuffer(new String[]{SpiderConst.DOUBAN_API_SEARCH_URL, SpiderConst.Q,
                    SpiderConst.EQUAL_FLAG, name});
            String response = null;

            int retryTimes = 0;
            do {
                if (!DouBanAppKeyPool.isBusy) {
                    // Max try 10 times
                    response = new DouBanUtil().sendRequestByAppKey(url.toString());
                    retryTimes++;
                    if (retryTimes > maxRetryTimes) {
                        log.info("Sync " + maxRetryTimes + " times failed for url " + url.toString() + ", ignore it");
                        break;
                    }
                } else {
                    // When appkey pool is busy, save update to DB and sleep 10
                    // seconds
                    if (updateList.size() > 0) {
                        douBanVideoBaseInfoManager.saveOrUpdate(updateList);
                        updateList = new ArrayList<DouBanVideoBaseInfo>();
                    }
                    try {
                        Thread.sleep(SpiderConst.THREAD_CHECK_INTERVAL_SECOND * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        log.info(e);
                    }
                }
            } while (response == null);

            // handle response
            if (response != null && !response.equals(SpiderConst.EMPTY)) {
                // Convert to object
                JSONObject json = JSONObject.fromObject(response);
                JsonConfig cfg = new JsonConfig();
                cfg.setPropertySetStrategy(new PropertyStrategyWrapper(PropertySetStrategy.DEFAULT));
                cfg.setRootClass(DouBanVideoBaseInfoJSON.class);
                DouBanVideoBaseInfoJSON infoJson = (DouBanVideoBaseInfoJSON) JSONObject.toBean(json, cfg);

                boolean isFound = false;
                for (Subject subject : infoJson.getSubjects()) {
                    if (!DouBanAppKeyPool.isBusy) {
                        // Gets subject details info by id
                        String[] aliasArray = null;
                        String aliasName = null;
                        StringBuffer subjectUrl = StringUtil.buildBuffer(new String[]{
                                SpiderConst.DOUBAN_API_SUBJECT_URL, subject.getId(), SpiderConst.QUESTION_MARK});
                        String subjectDetailsResponse = new DouBanUtil().sendRequestByAppKey(subjectUrl.toString());
                        if (subjectDetailsResponse != null && !subjectDetailsResponse.equals(SpiderConst.EMPTY)) {
                            // Gets subject alias name
                            int startIndex = subjectDetailsResponse.indexOf(SpiderConst.DOUBAN_AKA_FIELD);
                            if (startIndex != -1) {
                                int endIndex = subjectDetailsResponse.indexOf(SpiderConst.LEFT_SQUARE_BRACKET,
                                        startIndex);
                                aliasName = subjectDetailsResponse.substring(
                                        startIndex + SpiderConst.DOUBAN_AKA_FIELD.length(), endIndex);

                                // convert to array
                                if (aliasName != null && !aliasName.equals(SpiderConst.EMPTY)) {
                                    aliasArray = aliasName.substring(1, aliasName.length() - 1).split(
                                            SpiderConst.JSON_ARRAY_SPLIT);
                                }
                            }

                            // gets reviews count
                            int reviewsCount = 0;
                            String reviewsCountField = StringUtil.buildBuffer(
                                    new String[]{SpiderConst.DOUBLE_QUOTATION_MARKS, SpiderConst.REVIEWS_COUNT,
                                            SpiderConst.DOUBLE_QUOTATION_MARKS, SpiderConst.COLON}).toString();
                            startIndex = subjectDetailsResponse.indexOf(reviewsCountField);
                            if (startIndex != -1) {
                                int endIndex = subjectDetailsResponse.indexOf(SpiderConst.COMMA,
                                        reviewsCountField.length() + startIndex);
                                String reviewsCountStr = subjectDetailsResponse.substring(reviewsCountField.length()
                                        + startIndex, endIndex);
                                if (reviewsCountStr != null && !reviewsCountStr.equals(SpiderConst.EMPTY)) {
                                    reviewsCount = Integer.valueOf(reviewsCountStr);
                                }
                            }

                            if (isSameInfo(info, subject, aliasArray)) {
                                // Info and subject are the same video
                                if (equalsInfoName(info.getName(), subject.getTitle())) {
                                    info.setOriginal_title(subject.getOriginal_title());
                                } else {
                                    info.setOriginal_title(subject.getTitle());
                                }
                                String subType = subject.getSubtype();
                                info.setSubtype(subType != null && subType.equalsIgnoreCase(SpiderConst.TV) ? SpiderConst.TV_CHINESE
                                        : SpiderConst.MOVIE_CHINESE);
                                info.setYear(subject.getYear());
                                info.setSubject_id(subject.getId());
                                info.setScore(subject.getRating().getAverage());
                                info.setAliasName(aliasName);
                                info.setReviewsCount(reviewsCount);
                                isFound = true;
                                break;
                            }
                        }
                    } else {
                        break;
                    }
                }

                // if appKey pool is busy now, don't move to next element that
                // says don't make i++;
                if (!DouBanAppKeyPool.isBusy) {
                    i++;
                }

                info.setIsValid(isFound ? 1 : 0);
                updateList.add(info);
            } else {
                // always get null response for 10 times, ignore this info.
                i++;
                info.setIsValid(0);
            }
        }

        // Update to DB
        douBanVideoBaseInfoManager.saveOrUpdate(updateList);
    }

    private boolean isSameInfo(DouBanVideoBaseInfo info, Subject subject, String[] subjectAliasNames) {
        if (equalsSubType(info.getSubtype(), subject.getSubtype())) {
            if (equalsInfoName(info.getName(), subject.getTitle())) {
                return true;
            } else if (equalsInfoName(info.getName(), subject.getOriginal_title())) {
                return true;
            } else {
                if (subjectAliasNames != null && subjectAliasNames.length > 0) {
                    for (String name : subjectAliasNames) {
                        if (equalsInfoName(info.getName(), name)) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private boolean equalsInfoName(String infoName, String subjectTitle) {
        if (infoName == null || subjectTitle == null) {
            return false;
        } else {
            if (infoName.equals(subjectTitle)) {
                return true;
            }
        }
        return false;
    }

    private boolean equalsSubType(String infoSubType, String subjectSubType) {
        if (infoSubType == null || subjectSubType == null) {
            return true;
        } else {
            if (infoSubType.equalsIgnoreCase(SpiderConst.ANIME)
                    || infoSubType.equalsIgnoreCase(SpiderConst.ANIME_CHINESE)) {
                return true;
            } else if (infoSubType.equalsIgnoreCase(subjectSubType)) {
                return true;
            } else {
                if ((infoSubType.equalsIgnoreCase(SpiderConst.TV) || infoSubType
                        .equalsIgnoreCase(SpiderConst.TV_CHINESE))
                        && (subjectSubType.equalsIgnoreCase(SpiderConst.TV) || subjectSubType
                        .equalsIgnoreCase(SpiderConst.TV_CHINESE))) {
                    return true;
                } else if ((infoSubType.equalsIgnoreCase(SpiderConst.MOVIE) || infoSubType
                        .equalsIgnoreCase(SpiderConst.MOVIE_CHINESE))
                        && (subjectSubType.equalsIgnoreCase(SpiderConst.MOVIE) || subjectSubType
                        .equalsIgnoreCase(SpiderConst.MOVIE_CHINESE))) {
                    return true;
                }
            }
        }
        return false;
    }

    private String getFileFolderByDateTime(Date date) {
        String baseFolder = this.getInfoFolder() + File.separator + SpiderConst.BASE;
        File folder = new File(baseFolder);
        if (!folder.exists()) {
            folder.mkdir();
        }

        baseFolder = baseFolder + File.separator + SpiderConst.DOUBAN;
        folder = new File(baseFolder);
        if (!folder.exists()) {
            folder.mkdir();
        }

        SimpleDateFormat format = new SimpleDateFormat(SpiderConst.DATETIME_FORMAT);
        String dateTimeFolder = baseFolder + File.separator + format.format(date);
        folder = new File(dateTimeFolder);
        if (!folder.exists()) {
            folder.mkdir();
        }
        return dateTimeFolder;
    }

    private String convertToJson(DouBanVideoBaseInfo baseInfo) {
        JSONObject json = new JSONObject();
        json.put(SpiderConst.ID, baseInfo.getId());
        json.put(SpiderConst.NAME, baseInfo.getName());
        json.put(SpiderConst.ORIGINAL_NAME, baseInfo.getOriginal_title());
        json.put(SpiderConst.ALIAS_NAME, baseInfo.getAliasName());
        json.put(SpiderConst.DOUBANSCORE, baseInfo.getScore());
        json.put(SpiderConst.CATEGORY, baseInfo.getSubtype());
        return json.toString();
    }

    public DouBanVideoBaseInfoManager getDouBanVideoBaseInfoManager() {
        return douBanVideoBaseInfoManager;
    }

    public void setDouBanVideoBaseInfoManager(DouBanVideoBaseInfoManager douBanVideoBaseInfoManager) {
        this.douBanVideoBaseInfoManager = douBanVideoBaseInfoManager;
    }

    public String getInfoFolder() {
        return infoFolder;
    }

    public void setInfoFolder(String infoFolder) {
        this.infoFolder = infoFolder;
    }
}